package groovy

import groovy.sample.AssoTUser
import groovy.sample.TUser
import groovy.sample.X
import groovy.sample.userguide.closure.ClassHasEachDirMethod
import groovy.sample.userguide.closure.CloEnvB
import groovy.sample.userguide.closure.ClosureAcceptor
import groovy.sample.userguide.closure.HiddenMember
import groovy.sample.userguide.singleton.example1.Calculator
import groovy.sample.userguide.singleton.example1.CalculatorMetaClass
import groovy.sample.userguide.singleton.example1.Client
import groovy.sample.userguide.singleton.example2.Calculator1
import groovy.sample.userguide.singleton.example2.Calculator1Impl
import groovy.sample.userguide.singleton.example2.Client1
import groovy.sample.userguide.singleton.example3.Calculator2Impl
import groovy.sample.userguide.singleton.example3.Client2

import org.springframework.beans.factory.support.AbstractBeanDefinition
import org.springframework.beans.factory.support.DefaultListableBeanFactory
import org.springframework.beans.factory.support.RootBeanDefinition

import com.google.inject.Guice
import com.google.inject.Module
import com.thoughtworks.xstream.XStream

class UserGuide {

	static main(args) {
		def me = new UserGuide()
		me.collection()
	}
	
	//interfacing in groovy manner
	def advOO() {
		/*
		* the closure 'readable' will be executed when any method defined in Readable get called
		* and the param 'it', when Readable.read(charBuf) get called, represents the charBuf(since
		* read(charBuf) is the only method defined in Readable, 'it' always represent the charBuf),
		* which is exactly the object refered by s.buf as below.
		*/
		def readable = { it.put("12 34".reverse()); 5 } as Readable
		def s = new Scanner(readable)
		println s.buf
		assert s.nextInt() == 43
		println s.buf
		assert s.nextInt() == 2143
		println s.buf
		assert s.nextInt() == 2143
		println s.buf
		

		/*
		 * another evidence that the closure will be called accompany all interface methods 		
		 */
		def x = {Object[] args -> println "method called with args $args"} as X
		x.f()
		x.g(133)
		x.h("jeff", 2)
		println ""
		
		/*
		 * defined closure behavior in interface, will be executed for every interface method,
		 * what if we want to defined specific behavior for each method? use Map instead 
		 */
		def x1 = [f: {println "f called"}, h: {name, age -> println "h called"},] as X
		x1.f()
//		x1.g(123) //will throw "UnsupportedOperationException"
		x1.h("jeff", 2)
		
		
	}
	
	def annoSample() {
		//TODO: use annotation to implement XML serialization, lib required: XStream
		
		//step 1: serialize & deserialize without annotation
		def xstream = new XStream()
//		xstream.classLoader = getClass().classLoader //unnecessary
		def user1 = new TUser(id:2, name:"jeff", age:30)
		
		def xmlBean = xstream.toXML(user1) //serialized as String
		println xmlBean
		println ""
		
		def user2 = (TUser)xstream.fromXML(xmlBean)
		assert user2.name == "jeff"
		println user2.dump()
		println ""
		println ""
		
		
		//step 2: use annotation
		xstream.processAnnotations([AssoTUser])
		def user3 = new AssoTUser(id:3, name:"jeff", age:30)
		def xmlBean2 = xstream.toXML(user3)
		println xmlBean2
		println ""
		
		def user4 = (AssoTUser) xstream.fromXML(xmlBean2)
		assert user4.name == "jeff"
		println user4.dump()
	}
	
	def singletonPattern() {
		//TODO: lias to 3 ways to implement Singleton: MetaClass, Guice, Spring
		
		//example 1: using MetaProgramming
		GroovySystem.metaClassRegistry.setMetaClass(Calculator, new CalculatorMetaClass())
		def client = new Client()
		client.executeCalc(3, 5)
		println "$client, $client.calc, ${client.calc.getTotalCalculations()}"
		client.executeCalc(2, 1)
		println "$client, $client.calc, ${client.calc.getTotalCalculations()}"
		println ""
		
		//example 2: using guice
		//no annotation:
		def injector = Guice.createInjector([
				configure: { binding ->
						binding.bind(Calculator1).to(Calculator1Impl)
						.asEagerSingleton()
					}
			] as Module)
		
		def client1 = injector.getInstance(Client1)
		client1.executeCalc(3, 5)
		println "$client1, $client1.calc, ${client1.calc.getTotalCalculations()}"
		client1.executeCalc(2, 1)
		println "$client1, $client1.calc, ${client1.calc.getTotalCalculations()}"
		//this makes the unexpected result(bug?), means implemented class can do 
		//everything bypass its interface for binding.
		//a common sense logic is:
		//even if client can call fakeIt() bypassing the Calculator1 interface, 
		//such behavior should not be considered as valid behavior of "managed"
		//because it is transparent to the binding protocol. 
		client1.fakeCalc() 
		println "$client1, $client1.calc, ${client1.calc.getTotalCalculations()}"
		println ""
		
		//more simple implementation way is using annotation
		//@ImplementedBy(Calculator1Impl) interface Calculator1 {..}
		//@Singleton class Calculator1Impl {..}
		//def injector = Guice.createInjector()
		//....
		
		
		//example 3: using Spring autowiring(since we've been very familiar with the annotation way, here we try the programming way)
		
		// Here we 'wire' up our dependencies through the API. Alternatively,
		// we could use XML-based configuration or the Grails Bean Builder DSL.
		def calcImplBeanDef = new RootBeanDefinition(Calculator2Impl)
		def clientBeanDef = new RootBeanDefinition(Client2, false)
		clientBeanDef.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR)
		def factory = new DefaultListableBeanFactory()
		factory.registerBeanDefinition('calc', calcImplBeanDef)
		factory.registerBeanDefinition('client', clientBeanDef)
		
		def client2 = (Client2)factory.getBean('client')
		client2.executeCalc(3, 5)
		println "$client2, $client2.calc, ${client2.calc.getTotalCalculations()}"
		client2.executeCalc(2, 1)
		println "$client2, $client2.calc, ${client2.calc.getTotalCalculations()}"
		println ""
	}
	
	def bsf() {
		//don't know how to use the latest version, leave it later to study	
	}
	
	def closureSugar() {
		//parameter
		def closure1 = { p1, Object[] p2 ->
			println p1
			p2.each {print "$it "}
			println ""
		}
		closure1("jeff", "winnie", "cameron")
		
		//availability of enclosing environment
		def cloB = new CloEnvB()
		cloB.publicMethod("param") //works
		//cloB.publicMethod1("param") //doesn't work, since closure cannot back trace the call stack to
																//find envB's enclosing member ${bMember}
		
		//owner variable
		def hm = new HiddenMember()
		hm.getClosure1("dummy1")("dummy2")
		println ""
		
		//encountering creation: 2 closures created in the code below, closure
		//is not statically created and for multiple calls
		def clo1 = hm.getClosure1("dummy1")
		def clo2 = hm.getClosure1("dummy2")
		
		//launching closure
		clo1.call("dummy4") //same as clo1("dummy4")
		println ""
		
		//curry, easier ways to use a closure with too many args
		def cloPrnt = {arg1, arg2, arg3 -> println "$arg1 | $arg2 | $arg3"}
		def cloPrnt1 = cloPrnt.curry("jeff") //1st arg got permanently "known" as "jeff"
		cloPrnt1("winnie", "guoguo") //so caller no need to provide the 1st arg any more
		def cloPrnt2 = cloPrnt1.curry("winnie")
		cloPrnt2("guoguo")
		println ""
		
		//as function/method parameter
		def cloAcpt = new ClosureAcceptor()
		cloAcpt.accept1 {println it}
		cloAcpt.accept2("suffix") {println it}
		cloAcpt.accept2("suffix", {println it})
		cloAcpt.accept3 {println it} {println it}
		cloAcpt.accept3 ({println it}, {println it})
		//all above are legal
		def cloPrntAcpt = {println it}
		//cloAcpt.accept3 {println it} cloPrntAcpt //illegal
		cloAcpt.accept3 ({println it}, cloPrntAcpt) //legal
		println ""
		
		//as map[key : value]
		def cloMap = {println it}
		def map1 = [(cloMap) : 5]
		def map2 = [5 : cloMap]
		map2[5]("printed as map element")
		println ""
		
		//"use" directive
		def dir = new File("D:/temp")
		use(ClassHasEachDirMethod.class) {
			dir.eachDir {println it}
		}
	}
	
	def collection() {
		//list
		assert 1..10 instanceof java.util.List<Integer>
		assert [1,2,3,10] instanceof java.util.List<Integer>
		assert [1,2,3,10][3] == 10
		assert [1,2,3,10].get(2) == [1,2,3,10][2]
		
		//map
		def map1 = [1 : "jeff", 2 : "winnie", 3 : "guoguo"]
		assert map1[2] == "winnie"
		assert map1.get(2) == map1[2]
		assert map1.size() == 3
		map1.(4) = "yeye" //in bean style adding entry, the key(here is 4) will be treated as "4" string
		assert map1.size() == 4
		assert map1.get("4") == "yeye" //so, the key to get "yeye" is string "4" rather int 4
		
		// start-dot '*.' operator
		assert [1,3,5] == ["j", "jef", "jeff1"]*.size()
		
		// enhanced methods over java.util
		assert [1,2,3] == [0, 1, 2, 3].findAll {i -> i > 0}
		assert [false, false, true, true] == [0,1,2,3].collect {i -> i > 1}
		
		//infinite "index"
		assert "w" == "winnie"[0]
		assert "winnie" == "jeff winnie guoguo"[5..10]
		assert [2,3] == [0,1,2,3][2..3]
		assert "ldn" == "endless love"[3..1]
		assert "!" == "come on!"[-1]
		
		//Expendo, dynamic object
		def player = new Expando()
		player.name = "Jeff"
		player.greeting = {"Hi $name"}
		assert "Hi Jeff" == player.greeting()
		player.name = "Winnie"
		assert "Hi Winnie" == player.greeting()
	}

}
